package beautiful.butterfly.server.httpserver.handlers;

import beautiful.butterfly.server.application.Application;
import beautiful.butterfly.server.application.Environment;
import beautiful.butterfly.server.httpserver.mvc.core.ActionContext;
import beautiful.butterfly.server.httpserver.mvc.core.ActionExecute;
import beautiful.butterfly.server.httpserver.mvc.handler.ExceptionHandler;
import beautiful.butterfly.server.httpserver.mvc.http.HttpRequest;
import beautiful.butterfly.server.httpserver.mvc.http.HttpResponse;
import beautiful.butterfly.server.httpserver.mvc.http.Request;
import beautiful.butterfly.server.httpserver.mvc.http.Response;
import beautiful.butterfly.server.httpserver.mvc.render.RedirectRender;
import beautiful.butterfly.server.httpserver.mvc.render.Render;
import beautiful.butterfly.server.httpserver.mvc.render.TextRender;
import beautiful.butterfly.server.httpserver.mvc.render.VelocityRender;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import lombok.extern.slf4j.Slf4j;

import java.io.File;


@Slf4j
@ChannelHandler.Sharable
public class HttpServiceChannelInboundHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    static ActionExecute actionExecute = new ActionExecute(Application.getBasePackageName());
    private final String staticFilePath;
    //
    private final StaticFileHandler staticFileHandler;
    private final ExceptionHandler exceptionHandler;
    private SessionHandler sessionHandler = null;
    private Application application;
    private HttpServer httpServer;

    public HttpServiceChannelInboundHandler(Application application, HttpServer httpServer) {
        this.application = application;
        this.httpServer = httpServer;
        //
        this.staticFilePath = httpServer.getStaticFilePath();
        this.exceptionHandler = application.getExceptionHandler();
        this.staticFileHandler = new StaticFileHandler();
        //

        sessionHandler = new SessionHandler(httpServer.getSessionContent());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) {
        Request request = new HttpRequest(fullHttpRequest, this.httpServer, this.sessionHandler);
        Response response = HttpResponse.build(channelHandlerContext, null);
        ActionContext.setActionContext(this.httpServer, request, response);
        boolean isStatic = false;
        try {
            String uri = request.getUri();
            if (Application.developMode) {
                System.out.println("uri:" + uri);
            }
            if (!(uri.endsWith(".html") || "/".equals(uri))) {
                if (isValidateFile(uri)) {
                    request.setAttribute("$", Environment.pathFix(this.staticFilePath + uri));
                    staticFileHandler.handle(channelHandlerContext, request, response);
                    isStatic = true;
                    return;
                } else {
                    //
                    Render render = new TextRender("非法请求");
                    response.render(render);
                }
            } else {

                Render render = HttpServiceChannelInboundHandler.actionExecute.execute(request);
                if (render == null) {
                    render = new TextRender("404");
                    response.render(render);
                } else {
                    if (render instanceof TextRender) {
                        response.render(render);
                    } else if (render instanceof VelocityRender) {
                        response.render(render);
                    } else if (render instanceof RedirectRender) {
                        response.redirect(render.render());
                    } else {
                        throw new IllegalArgumentException("else");
                    }
                }
            }

            //
            this.sendFinish(response);
        } catch (Exception e) {
            e.printStackTrace();
            if (null != exceptionHandler) {
                exceptionHandler.handle(e);
            } else {
                log.error("Application Invoke Error", e);
            }
        } finally {
            if (!isStatic) {
                this.sendFinish(response);
            }
            ActionContext.remove();

        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) {
        if (channelHandlerContext.channel().isOpen() && channelHandlerContext.channel().isActive() && channelHandlerContext.channel().isWritable()) {
            channelHandlerContext.flush();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        if (null != exceptionHandler) {
            exceptionHandler.handle((Exception) cause);
        } else {
            log.error("Application Invoke Error", cause);
        }
        if (channelHandlerContext.channel().isOpen() &&//
                channelHandlerContext.channel().isActive() &&//
                channelHandlerContext.channel().isWritable()) {
            channelHandlerContext.close();
        }
    }

    private boolean isValidateFile(String uri) {
        String filePath = this.staticFilePath + uri;
        File file = new File(filePath);
        if (file.exists()) {
            String path = file.getAbsolutePath();
            if (!Environment.pathFix(path).startsWith(this.staticFilePath)) {//防止linux路径遍历漏洞
                throw new IllegalArgumentException("非法的文件路径");
            }
            if (file.isHidden()) {
                return false;
            }
            if (file.isDirectory()) {
                return false;
            }
            if (!file.isFile()) {
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    private void sendFinish(Response response) {
        if (!response.isCommit()) {
            response.body(Unpooled.EMPTY_BUFFER);
        }
    }

}